{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "05e0688c",
   "metadata": {},
   "source": [
    "# Streamlit 程序执行流程，session states的使用以及组件函数回调\n",
    "\n",
    "https://docs.streamlit.io/library/advanced-features/session-state\n",
    "\n",
    "* 什么是状态（state）？\n",
    "\n",
    "我们将浏览器选项卡中对 Streamlit 应用程序的访问定义为会话。 对于连接到 Streamlit 服务器的每个浏览器选项卡，都会创建一个新会话。 每次与应用交互时，Streamlit 都会从上到下重新运行脚本。每次重新运行都在空白状态下进行：运行之间不共享任何变量。\n",
    "\n",
    "会话状态是一种在重新运行之间为每个用户会话共享变量的方法。 除了存储和持久化状态的能力之外，Streamlit 还公开了使用回调操作状态的能力。 会话状态还在多页面应用程序中跨应用程序持续存在。\n",
    "\n",
    "有关会话状态和回调 API 的详细信息，请参阅我们的会话状态 API 参考指南。\n",
    "\n",
    "https://docs.streamlit.io/library/api-reference/session-state\n",
    "\n",
    "* 我们将在构建有状态计数器应用程序时说明会话状态和回调的用法。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a7d2f12",
   "metadata": {},
   "outputs": [],
   "source": [
    "import streamlit as st\n",
    "\n",
    "st.title('Counter Example')\n",
    "count = 0\n",
    "\n",
    "increment = st.button('Increment')\n",
    "if increment:\n",
    "    count += 1\n",
    "\n",
    "st.write('Count = ', count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1787deac",
   "metadata": {},
   "source": [
    "无论我们在上面的应用程序中按多少次增量按钮，计数都保持为 1。让我们了解原因：\n",
    "\n",
    "* 每次我们按下增量按钮时，Streamlit 都会从上到下重新运行 counter.py，并且每次运行时，count 都会初始化为 0。\n",
    "\n",
    "* 按增量随后将 1 加到 0，因此无论我们按多少次增量，计数 = 1。\n",
    "\n",
    "正如我们稍后将看到的，我们可以通过将计数存储为会话状态变量来避免此问题。 通过这样做，我们向 Streamlit 表明它应该在应用程序重新运行时维护存储在会话状态变量中的值。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e26ed8b",
   "metadata": {},
   "source": [
    "### 初始化\n",
    "Session State API 遵循基于字段的 API，这与 Python 字典非常相似。其中的变量独立于app，在app倍重新运行后仍然能存储之前的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee162b29",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Check if 'key' already exists in session_state\n",
    "# If not, then initialize it\n",
    "if 'key' not in st.session_state:\n",
    "    st.session_state['key'] = 'value'\n",
    "\n",
    "# Session State also supports the attribute based syntax\n",
    "if 'key' not in st.session_state:\n",
    "    st.session_state.key = 'value'"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9bc26b67",
   "metadata": {},
   "source": [
    "### 读取与更新\n",
    "\n",
    "通过将项目传递给 st.write 来读取会话状态中项目的值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1918d436",
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'key' not in st.session_state:\n",
    "    st.session_state['key'] = 'value'\n",
    "\n",
    "# Reads\n",
    "st.write(st.session_state.key)\n",
    "# Outputs: value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7cc529b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "通过为其分配一个值来更新会话状态中的项目"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d79a2bde",
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'key' not in st.session_state:\n",
    "    st.session_state['key'] = 'value'\n",
    "\n",
    "# Updates\n",
    "st.session_state.key = 'value2'     # Attribute API\n",
    "st.session_state['key'] = 'value2'  # Dictionary like API"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bc4cd2ed",
   "metadata": {},
   "source": [
    "### streamlit的运行机制及其代码位置的重要性\n",
    "\n",
    "classwork 1\n",
    "\n",
    "* 通过st.session_state完成上面加1按钮的改写，看看效果是否有所不同\n",
    "\n",
    "* 在按钮上增加一个输出，观察两个输出的不同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "983f84bb",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "15738e66",
   "metadata": {},
   "source": [
    "## 回调与会话状态\n",
    "\n",
    "回调：回调是一个 Python 函数，它在输入小部件更改时被调用。 可以使用参数 on_change（或 on_click）、args 和 kwargs 将回调与小部件一起使用。 完整的回调 API 可以在我们的会话状态 API 参考指南中找到。\n",
    "\n",
    "https://docs.streamlit.io/library/api-reference/session-state#use-callbacks-to-update-session-state\n",
    "\n",
    "* 注意在app因为改变而重新执行时，组件所对应的函数始终是首先执行的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6799a04",
   "metadata": {},
   "outputs": [],
   "source": [
    "st.title('Counter Example using Callbacks')\n",
    "if 'count' not in st.session_state:\n",
    "    st.session_state.count = 0\n",
    "\n",
    "st.write('Count = ', st.session_state.count)\n",
    "\n",
    "def increment_counter():\n",
    "    st.session_state.count += 1\n",
    "\n",
    "st.button('Increment', on_click=increment_counter)\n",
    "\n",
    "st.write('Count = ', st.session_state.count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9a764677",
   "metadata": {},
   "source": [
    "回调还支持在小部件中使用 args 参数传递参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03e23fae",
   "metadata": {},
   "outputs": [],
   "source": [
    "import streamlit as st\n",
    "\n",
    "st.title('Counter Example using Callbacks with args')\n",
    "if 'count' not in st.session_state:\n",
    "    st.session_state.count = 0\n",
    "\n",
    "increment_value = st.number_input('Enter a value', value=0, step=1)\n",
    "\n",
    "def increment_counter(increment_value):\n",
    "    st.session_state.count += increment_value\n",
    "\n",
    "increment = st.button('Increment', on_click=increment_counter,\n",
    "    args=(increment_value, ))\n",
    "\n",
    "st.write('Count = ', st.session_state.count)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f113bc1d",
   "metadata": {},
   "source": [
    "此外，我们还可以在小部件中使用 kwargs 参数将命名参数传递给回调函数，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c056866",
   "metadata": {},
   "outputs": [],
   "source": [
    "import streamlit as st\n",
    "\n",
    "st.title('Counter Example using Callbacks with kwargs')\n",
    "if 'count' not in st.session_state:\n",
    "    st.session_state.count = 0\n",
    "\n",
    "def increment_counter(increment_value=0):\n",
    "    st.session_state.count += increment_value\n",
    "\n",
    "def decrement_counter(decrement_value=0):\n",
    "    st.session_state.count -= decrement_value\n",
    "\n",
    "st.button('Increment', on_click=increment_counter,\n",
    "    kwargs=dict(increment_value=5))\n",
    "\n",
    "st.button('Decrement', on_click=decrement_counter,\n",
    "    kwargs=dict(decrement_value=1))\n",
    "\n",
    "st.write('Count = ', st.session_state.count)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "39095f74",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'increment_value': 5}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dict(increment_value=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e97d1e5f",
   "metadata": {},
   "source": [
    "### classwork 2-代码位置的作业\n",
    "\n",
    "* 两种方式来实现位置与输出的分离，1，通过函数回调 2，st.container\n",
    "\n",
    "1， 请分别用条件的方式与函数回调的方式实现对男鞋数据表格展示行数的增减控制\n",
    "\n",
    "2， 请尝试使用st.container来展现数据表格，通过另一种方式实现输出位置与程序位置的分离"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "07ead93f",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyecharts.faker import Faker\n",
    "import json\n",
    "from streamlit_echarts import Map\n",
    "from streamlit_echarts import JsCode\n",
    "import streamlit as st\n",
    "from streamlit_echarts import st_echarts\n",
    "from pyecharts import options as opts\n",
    "from pyecharts.charts import Bar\n",
    "from streamlit_echarts import st_pyecharts\n",
    "from pyecharts.charts import Line, Pie, Bar\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "a=pd.read_csv(r\"D:\\try\\shoes.csv\")\n",
    "a.sales=a.sales.str.replace(\"人付款\",\"\").astype(int)\n",
    "#求出各个商品的销售额并把它并入到原始数据框中去\n",
    "z1=a.sales*a.price\n",
    "z1.name=\"xse\"\n",
    "a=pd.concat([a,z1],axis=1)#给序列命名之后添加入数据框就会直接以序列名作为列标\n",
    "location=a.location.str.split(\" \",expand=True)\n",
    "a[\"province\"]=location[0]\n",
    "a[\"city\"]=location[1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ccf5dad1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Index(['_id.$oid', 'info.上市年份季节', 'info.上市时间', 'info.产品名称', 'info.低帮鞋品名',\n",
       "       'info.功能', 'info.吊牌价', 'info.品牌', 'info.图案', 'info.场合', 'info.外底材料',\n",
       "       'info.季节', 'info.尺码', 'info.帮面内里材质', 'info.帮面材质', 'info.性别',\n",
       "       'info.是否商场同款', 'info.是否瑕疵', 'info.款号', 'info.款式', 'info.流行元素',\n",
       "       'info.真皮材质工艺', 'info.细分风格', 'info.货号', 'info.跟底款式', 'info.运动系列',\n",
       "       'info.运动鞋科技', 'info.适合路面', 'info.适用对象', 'info.销售渠道类型', 'info.闭合方式',\n",
       "       'info.靴子品名', 'info.靴筒内里材质', 'info.靴筒材质', 'info.靴筒高度', 'info.鞋制作工艺',\n",
       "       'info.鞋垫材质', 'info.鞋头款式', 'info.鞋底材质', 'info.鞋码', 'info.鞋跟高',\n",
       "       'info.鞋面内里材质', 'info.鞋面材质', 'info.颜色分类', 'info.风格', 'itemid',\n",
       "       'location', 'nick', 'price', 'sales', 'title', 'url', 'xse', 'province',\n",
       "       'city'],\n",
       "      dtype='object')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a.columns"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c403873",
   "metadata": {},
   "source": [
    "### 会话状态和组件状态\n",
    "\n",
    "\n",
    "会话状态提供了跨重新运行存储变量的功能。 组件状态（即组件的值）也存储在会话中。\n",
    "\n",
    "为简单起见，我们将这些信息统一在一个地方。 即会话状态。 这种便利的功能使得在应用程序代码中的任何位置读取或写入小部件的状态变得超级容易。 会话状态变量使用 key 参数镜像小部件值。\n",
    "\n",
    "我们用下面的例子来说明这一点。 假设我们有一个带有滑块的应用程序来表示摄氏温度。 我们可以使用 Session State API 设置和获取温度小部件的值，如下所示："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e526afc4",
   "metadata": {},
   "outputs": [],
   "source": [
    "import streamlit as st\n",
    "\n",
    "if \"celsius\" not in st.session_state:\n",
    "    # set the initial default value of the slider widget\n",
    "    st.session_state.celsius = 50.0\n",
    "\n",
    "st.slider(\n",
    "    \"Temperature in Celsius\",\n",
    "    min_value=-100.0,\n",
    "    max_value=100.0,\n",
    "    key=\"celsius\"\n",
    ")\n",
    "\n",
    "# This will get the value of the slider widget\n",
    "st.write(st.session_state.celsius)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a863ce1",
   "metadata": {},
   "source": [
    "Streamlit 不允许通过会话状态 API 为 st.button 和 st.file_uploader 设置组件值。\n",
    "下面的代码将运行错误"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a80a558",
   "metadata": {},
   "outputs": [],
   "source": [
    "if 'my_button' not in st.session_state:\n",
    "    st.session_state.my_button = True\n",
    "    # Streamlit will raise an Exception on trying to set the state of button\n",
    "\n",
    "st.button('Submit', key='my_button')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e7e604d",
   "metadata": {},
   "source": [
    "### classwork3-组件之间的通信\n",
    "\n",
    "* 分布通过st.button和st.checkbox控制男鞋数据的显示，了解其中的不同效果\n",
    "\n",
    "* 分别统计类别和数值两类特征，对于类别特征输出其柱图，对于数值特征输出describe统计结果，通过st.selectbox选择特征，通过st.radio选择特征类别\n",
    "\n",
    "'info.鞋面材质','info.风格', 'province','city','price', 'sales','xse',"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "33db164c",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
